package com.cuitrip.util.file;

import android.content.Context;

import com.lab.logtrack.AppLog;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

/**
 * Created by ak
 */
public class FileUtil {
    /**
     * 解析文件时，默认每次读取4K
     */
    public static final int DEFAULT_READ_PER_TIME = 4096;


    /**
     * 删除文件
     *
     * @param path 文件绝对路径
     */
    public static void deleteFile(String path) {
        File file = new File(path);
        deleteFile(file);
    }

    /**
     * 删除文件
     *
     * @param
     */
    public static void deleteFile(File file) {

        if (file.exists()) {
            if (file.isFile()) {
                file.delete();
            } else if (file.isDirectory()) {
                File files[] = file.listFiles();
                if (files != null && files.length > 0) {
                    for (int i = 0; i < files.length; i++) {
                        deleteFile(files[i]);
                    }
                }
                file.delete();
            }
        } else {
            AppLog.e("所删除的文件不存在！");
        }
    }



    /**
     * 读取文件长度
     * <p/>
     * RandomAccessFile
     * FileChannel
     * FileStream
     * length()
     * 对比 RandomAccessFile 最优{#ReadMe 需要进一步验证}
     *
     * @param file
     * @return
     */
    public static long getFileLength(File file) {
        if (file == null || !file.exists() || !file.isFile()) {
            return 0;
        }
//        return file.length();
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(file, "r");
            long length = raf.length();
            raf.close();
            return length;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 读取文件部分内容
     *
     * @param file   文件对象
     * @param offset 偏移量
     * @param length 读取长度（最多支持4096）
     * @return
     */
    public static byte[] readFileData(File file, int offset, int length) {
        ByteArrayOutputStream byte_out = null;
        RandomAccessFile bufferedRandomAccessFile = null;
        try {
            bufferedRandomAccessFile = new RandomAccessFile(file, "r");
            bufferedRandomAccessFile.seek(offset);
            byte_out = new ByteArrayOutputStream();
            byte buff[] = new byte[length];
            int i = 0;
            if ((i = bufferedRandomAccessFile.read(buff, 0, length)) != -1) {
                byte_out.write(buff, 0, i);
            }
            return byte_out.toByteArray();
        } catch (IOException e) {
            AppLog.e(e.getMessage());
        } finally {
            if (byte_out != null) {
                try {
                    byte_out.close();
                } catch (IOException e) {
                    AppLog.e(e.getMessage());
                }
            }
            if (bufferedRandomAccessFile != null) {
                try {
                    bufferedRandomAccessFile.close();
                } catch (IOException e) {
                    AppLog.e(e.getMessage());
                }
            }
        }
        return null;
    }

    /**
     * 获取文件 实际内容
     *
     * @return
     */
    public static byte[] readFileData(File file, int offset) {
        ByteArrayOutputStream byte_out = null;
        RandomAccessFile bufferedRandomAccessFile = null;
        try {
            bufferedRandomAccessFile = new RandomAccessFile(file, "r");
            bufferedRandomAccessFile.seek(offset);
            byte_out = new ByteArrayOutputStream();
            byte buff[] = new byte[DEFAULT_READ_PER_TIME];
            int i = 0;
            while ((i = bufferedRandomAccessFile.read(buff, 0, DEFAULT_READ_PER_TIME)) != -1) {
                byte_out.write(buff, 0, i);
            }
            return byte_out.toByteArray();
        } catch (IOException e) {
            AppLog.e(e.getMessage());
        } finally {
            if (byte_out != null) {
                try {
                    byte_out.close();
                } catch (IOException e) {
                    AppLog.e(e.getMessage());
                }
            }
            if (bufferedRandomAccessFile != null) {
                try {
                    bufferedRandomAccessFile.close();
                } catch (IOException e) {
                    AppLog.e(e.getMessage());
                }
            }
        }
        return null;
    }

    public static boolean writeFileDataWithLock(File file, byte[] data, int offset) {
        RandomAccessFile randomAccessFile = null;
        FileChannel fileChannel = null;
        FileLock fileLock = null;
        try {
            randomAccessFile = new RandomAccessFile(file, "r");
            randomAccessFile.seek(offset);
            fileChannel = randomAccessFile.getChannel();
            fileLock = fileChannel.lock();
            fileChannel.write(ByteBuffer.wrap(data));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileLock != null) {
                try {
                    fileLock.release();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileChannel != null) {
                try {
                    fileChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    /**
     * 写入文件内容
     *
     * @param file
     * @param data
     * @return
     */
    public static boolean writeFileData(File file, byte[] data, int offset) {
        RandomAccessFile bufferedRandomAccessFile = null;
        try {
            bufferedRandomAccessFile = new RandomAccessFile(file, "rw");
            bufferedRandomAccessFile.seek(offset);
            bufferedRandomAccessFile.write(data);
//            ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
//            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
//            DataOutputStream dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
//            byte[] buffer = new byte[DEFAULT_READ_PER_TIME];
//            int length = 0;
//            while ((length = bufferedInputStream.read(buffer))!=-1){
//                dataOutputStream.write(buffer,0,length);
//            }
//            dataOutputStream.close();
//            bufferedInputStream.close();
            return true;
        } catch (IOException e) {
            AppLog.e(e.getMessage());
        } finally {
            if (bufferedRandomAccessFile != null) {
                try {
                    bufferedRandomAccessFile.close();
                } catch (IOException e) {
                    AppLog.e(e.getMessage());
                }
            }
        }
        return false;
    }



    /**
     * 获取文件内容
     * #FIXME 使用NIO，Native内存如何释放（已解决）
     * #README 添加so库，解决问题，性能待测试，已测试(小文件对比，NIO并不比Stream快，可能写法导致)
     *
     * @return
     */
    public static byte[] readFileDataNIO(File file, int offset) {
        FileChannel channel = null;
        RandomAccessFile fis = null;
        ByteBuffer byteBuffer = null;
        ByteArrayOutputStream byteOut = null;
        try {
            fis = new RandomAccessFile(file, "rw");
            channel = fis.getChannel();
            channel.position(offset);
            byte[] buffer = new byte[DEFAULT_READ_PER_TIME];
            byteBuffer = ByteBuffer.wrap(buffer);
            int readLength = 0;
            byteOut = new ByteArrayOutputStream();
            while ((readLength = channel.read(byteBuffer)) != -1) {
                byteBuffer.flip();
                byteOut.write(byteBuffer.array(), 0, readLength);
                byteBuffer.clear();
            }
            byte[] bytes = byteOut.toByteArray();
            return bytes;
        } catch (Exception e) {
            e.printStackTrace();
            AppLog.e(e.getMessage());
        } finally {
            if (channel != null) {
                // 关闭通道
                try {
                    channel.close();
                } catch (IOException e) {
                    AppLog.e(e.getMessage());
                }
            }
            if (fis != null) {
                // 关闭文件流
                try {
                    fis.close();
                } catch (IOException e) {
                    AppLog.e(e.getMessage());
                }
            }
//            if (byteBuffer != null) {
//                DirectMemoryUtil.freeDirect(byteBuffer);
//            }
            if (byteOut != null) {
                // 关闭文件流
                try {
                    byteOut.close();
                } catch (IOException e) {
                    AppLog.e(e.getMessage());
                }
            }
        }
        return null;
    }

    /**
     * 获取文件hash
     *
     * @param file
     * @return
     */
    public static String calFileHash(File file) {
        if (!file.exists() || !file.isFile()) {
            return null;
        }
        return String.valueOf(file.getAbsolutePath().hashCode());
    }

    /**
     * 确保目录可用，或返回备用目录如果正常目录不可用，递归上限5次
     * @param dir 想要使用的目录
     * @param time 当前递归次数
     * @param recursionLimit 递归上限
     * @return
     */
    public static File ensureDirectoryOrBackups(File dir,int time,int recursionLimit){
        //当前目录可用
        if(dir == null || dir.exists() || dir.mkdirs()){
            return dir;
        }

        //当前目录的备用目录可用
        File bkdir = new File(dir.getParentFile(),"._"+dir.getName());
        if(bkdir.exists()||bkdir.mkdirs()){
            return bkdir;
        }

        //递归查找父目录
        File parent = time>recursionLimit?null:ensureDirectoryOrBackups(dir.getParentFile(),time+1,recursionLimit);

        //找到父目录 创建子目录
        if(parent!=null){
            dir = new File(parent,dir.getName());
            if(dir.exists()||dir.mkdirs()){
                return dir;
            }
        }
        return null;
    }

    /**
     * 读取assets资源, 返回byte流
     * @param context
     * @param fileName
     * @return
     */
    public static byte[] getFromAssets(Context context, String fileName){
        byte[] buffer = null;
        InputStream in = null;
        try {
            in = context.getResources().getAssets().open(fileName);
            int lengh = in.available();
            buffer = new byte[lengh];
            in.read(buffer);
        } catch (Exception e) {
            AppLog.e(e.getMessage());
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return buffer;
    }
}
